const tencentcloud = require("tencentcloud-sdk-nodejs-ssl");
const fs = require("fs");
const path = require("path");
const https = require("https");
const unzipper = require("unzipper");
const { exec } = require("child_process");
const logger = require("./logs");
const { CronJob } = require("cron");
const config = require("./config");
const SslClient = tencentcloud.ssl.v20191205.Client;

// 配置腾讯云证书申请
const clientConfig = {
  credential: {
    secretId: config.credential.secretId, // 填入你的 secretId
    secretKey: config.credential.secretKey, // 填入你的 secretKey
  },
  profile: {
    httpProfile: {
      endpoint: "ssl.tencentcloudapi.com",
    },
  },
};

// 申请证书，修改为返回 Promise
const createCrt = () => {
  return new Promise((resolve, reject) => {
    const client = new SslClient(clientConfig);
    const params = {
      DomainName: config.sslFileBaseName,
      DvAuthMethod: "DNS",
    };
    client.ApplyCertificate(params).then(
      (data) => {
        logger.info("申请证书成功", data);
        resolve(data); // 证书申请成功，返回数据
      },
      (err) => {
        logger.error("申请证书失败", err);
        reject(err); // 证书申请失败，返回错误
      }
    );
  });
};

// 查询证书状态
const queryCrtStatus = (certificateId) => {
  const client = new SslClient(clientConfig);
  const params = {
    CertificateId: certificateId,
  };
  return client.CheckCertificateDomainVerification(params).then(
    (data) => {
      if (data && data.VerificationResults.length > 0) {
        if (data.VerificationResults[0].Issued) {
          logger.info("证书已签发成功", certificateId);
          return true;
        }
      }
      return false;
    },
    (err) => {
      logger.error("查询证书状态失败", err);
      return false;
    }
  );
};

// 获取最新的有效证书
const getCrtInfo = () => {
  const client = new SslClient(clientConfig);
  const params = {};
  return client.DescribeCertificates(params).then(
    (data) => {
      if (data.TotalCount > 0) {
        const list = data.Certificates.filter((r) => r.Status == 1);
        if (list.length > 0) {
          const crts = list.sort((a, b) => {
            return new Date(b.IssueTime) - new Date(a.IssueTime);
          });
          const crt = crts[0];
          logger.info("获取到最新的证书", crt);
          return crt;
        }
      }
      return null;
    },
    (err) => {
      logger.error("获取证书信息失败", err);
      return null;
    }
  );
};

// 获取下载链接
const getDownLoadUrl = (certificateId) => {
  const client = new SslClient(clientConfig);
  const params = {
    CertificateId: certificateId,
    ServiceType: "nginx",
  };
  return client.DescribeDownloadCertificateUrl(params).then(
    (data) => {
      logger.info("下载证书链接获取成功", data);
      return data.DownloadCertificateUrl;
    },
    (err) => {
      logger.error("获取下载证书链接失败", err);
      return null;
    }
  );
};

// 下载并解压证书
const downloadAndUnzipCert = (url, destDir) => {
  const fileName = path.basename(url);
  const filePath = path.join(destDir, fileName);

  return new Promise((resolve, reject) => {
    https.get(url, (response) => {
      const unzipStream = unzipper.Extract({ path: destDir });
      response.pipe(unzipStream);

      unzipStream.on("close", () => {
        logger.info(`证书文件下载并解压成功: ${filePath}`);
        resolve(filePath); // 返回文件路径
      });

      unzipStream.on("error", (err) => {
        logger.error("解压证书文件失败", err);
        reject(err);
      });
    });
  });
};

// 复制目录中的所有文件到目标目录
const copyFilesInDir = (dirname, targetDir) => {
  return new Promise((resolve, reject) => {
    // 读取源目录中的所有文件
    fs.readdir(dirname, (err, files) => {
      if (err) {
        logger.error("读取目录失败", err);
        reject(`读取目录失败: ${err}`);
        return;
      }

      // 创建目标目录（如果目标目录不存在）
      if (!fs.existsSync(targetDir)) {
        fs.mkdirSync(targetDir, { recursive: true });
      }

      let copyPromises = [];

      // 遍历源目录中的文件
      files.forEach((file) => {
        const srcPath = path.join(dirname, file);
        const destPath = path.join(targetDir, file);

        // 只复制文件（跳过子目录）
        if (fs.lstatSync(srcPath).isFile()) {
          // 将文件复制到目标目录
          const copyPromise = new Promise((resolveCopy, rejectCopy) => {
            fs.copyFile(srcPath, destPath, (copyErr) => {
              if (copyErr) {
                logger.error(`文件复制失败: ${file}`, copyErr);
                rejectCopy(copyErr);
              } else {
                logger.info(`文件已复制: ${file} 到 ${destPath}`);
                resolveCopy();
              }
            });
          });

          copyPromises.push(copyPromise);
        }
      });

      // 等待所有文件复制完成
      Promise.all(copyPromises)
        .then(() => {
          resolve(`所有文件已成功复制到目标目录: ${targetDir}`);
        })
        .catch((err) => {
          reject(`复制文件失败: ${err}`);
        });
    });
  });
};

// 重启 nginx
const restartNginx = () => {
  exec("nginx -s reload", (err, stdout, stderr) => {
    if (err) {
      logger.error("重启 nginx 失败", stderr);
    } else {
      logger.info("nginx 重启成功", stdout);
    }
  });
};

// 轮询查询签发状态
const waitForCrtIssue = async (certificateId) => {
  let attempt = 0;
  const maxAttempts = 5;
  const interval = 60000; // 1分钟

  while (attempt < maxAttempts) {
    logger.info(`轮询签发状态，尝试次数：${attempt + 1}`);
    const issued = await queryCrtStatus(certificateId);
    if (issued) {
      logger.info("证书签发成功");
      return true;
    }

    attempt++;
    if (attempt < maxAttempts) {
      logger.info("等待1分钟后继续查询...");
      await new Promise((resolve) => setTimeout(resolve, interval));
    }
  }

  logger.error("证书签发失败，已达到最大尝试次数");
  return false;
};

// 修改指定目录中所有文件的文件名（basename）
const renameFilesInDir = (dirname, newBasename) => {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, files) => {
      if (err) {
        reject(`读取目录失败: ${err}`);
        return;
      }

      let renamedFiles = [];

      files.forEach((file, index) => {
        const filePath = path.join(dirname, file);
        const extname = path.extname(file); // 获取文件扩展名
        const newFileName = `${newBasename}${extname}`; // 新文件名
        const newFilePath = path.join(dirname, newFileName);

        // 只对文件进行重命名（跳过子目录）
        if (fs.lstatSync(filePath).isFile()) {
          // 重命名文件
          fs.rename(filePath, newFilePath, (renameErr) => {
            if (renameErr) {
              console.error(`文件重命名失败: ${file}`, renameErr);
            } else {
              renamedFiles.push(newFilePath); // 将重命名后的文件记录下来
            }

            // 当所有文件都重命名完成时，返回结果
            if (renamedFiles.length === files.length) {
              resolve(renamedFiles);
            }
          });
        }
      });
    });
  });
};

// 定时任务：每天晚上12点执行证书检查和更新
const job = new CronJob(
  "0 0 0 * * *",
  async () => {
    try {
      const crt = await getCrtInfo();
      if (crt) {
        const issueTime = new Date(crt.IssueTime);
        const now = new Date();
        const diffDays = (issueTime - now) / (1000 * 3600 * 24);

        if (diffDays < 3) {
          logger.info("证书有效期少于3天，申请新的证书...");
          const crtData = await createCrt(); // 申请新的证书

          // 轮询检查证书签发状态
          const issueSuccess = await waitForCrtIssue(crtData.CertificateId);
          if (issueSuccess) {
            // 获取下载链接
            const downloadUrl = await getDownLoadUrl(crtData.CertificateId);
            if (downloadUrl) {
              const destDir = config.sslFileBaseName; // 修改为实际的 nginx cert 目录
              const filePath = await downloadAndUnzipCert(downloadUrl, destDir); // 下载并解压证书

              // 遍历目录
              renameFilesInDir(filePath, config.sslFileBaseName);

              // 复制证书文件
              await copyFilesInDir(filePath, destDir);

              // 重启 nginx
              restartNginx();
            }
          } else {
            logger.error("证书签发失败，未进行后续操作");
          }
        } else {
          logger.info("证书有效期正常，不需要更新");
        }
      }
    } catch (err) {
      logger.error("定时任务执行失败", err);
    }
  },
  null,
  true,
  "Asia/Shanghai"
);

// 启动定时任务
job.start();
